
from django.contrib.gis.db import models
from django.db.models import Max, Min
import datetime

def seconds_to_time(seconds):
    minutes = seconds / 60
    seconds -= minutes * 60
    hours = minutes / 60
    minutes -= hours * 60
    return datetime.time(hour=hours, minute=minutes, second=seconds)

class Dataset(models.Model):
    source = models.CharField(max_length=128)
    file_path = models.CharField(max_length=256)
    imported = models.DateTimeField()

    def __str__(self):
        return self.source + "/" + str(self.imported.date())

class Agency(models.Model):
    dataset = models.ForeignKey('Dataset')
    text_id = models.CharField(max_length=20, db_index=True)
    name = models.TextField()
    url = models.URLField()
    timezone = models.CharField(max_length=40)
    lang = models.CharField(max_length=2)
    phone = models.CharField(max_length=10)

    def __str__(self):
        return self.name

    class Meta:
        unique_together = (("dataset", "text_id"))

class Stop(models.Model):
    dataset = models.ForeignKey('Dataset')
    text_id = models.CharField(max_length=20, db_index=True)
    code = models.TextField()
    name = models.TextField()
    desc = models.TextField()
    point = models.PointField()
    zone = models.ForeignKey('Zone', null=True)
    url = models.URLField()
    is_station = models.BooleanField()
    parent_station = models.ForeignKey('Stop', null=True)

    def __str__(self):
        return self.name

    class Meta:
        unique_together = (("dataset", "text_id"))

    def timetable(self, dt):
        for stop_time in self.stoptime_set.all().order_by('arrival_days', 'arrival_time', 'sequence'):
            route = stop_time.trip.route
            service = stop_time.trip.service
            # we check active for the date this particular trip started, not for today
            if service.active(dt - datetime.timedelta(days=stop_time.arrival_days)):
                yield stop_time, route

class RouteType(models.Model):
    desc = models.TextField()
    name = models.TextField()

class Route(models.Model):
    text_id = models.CharField(max_length=20, db_index=True)
    agency = models.ForeignKey('Agency')
    short_name = models.TextField()
    long_name = models.TextField()
    desc = models.TextField()
    route_type = models.ForeignKey('RouteType')
    url = models.URLField(max_length=1000)
    colour = models.TextField()
    text_colour = models.TextField()

    class Meta:
        unique_together = (("agency", "text_id"))

class Block(models.Model):
    dataset = models.ForeignKey('Dataset')
    text_id = models.TextField(max_length=20, db_index=True)

    class Meta:
        unique_together = (("dataset", "text_id"))

class Trip(models.Model):
    text_id = models.CharField(max_length=20, db_index=True)
    route = models.ForeignKey('Route')
    service = models.ForeignKey('Service')
    headsign = models.TextField()
    short_name = models.TextField()
    inbound = models.BooleanField()
    outbound = models.BooleanField()
    block = models.ForeignKey('Block', null=True)
    shape = models.ForeignKey('Shape', null=True)

    class Meta:
        unique_together = (("service", "text_id"), ("route", "text_id"))

    def frequency(self, from_date, to_date):
        count = 0
        a_day = datetime.timedelta(days=1)
        dt = from_date
        agg = self.stoptime_set.aggregate(Min('sequence'))
        first_stoptime = self.stoptime_set.get(sequence=agg['sequence__min'])
        first_stoptime_offset = (first_stoptime.arrival_days * a_day)

        while dt <= to_date:
            if self.service.active(dt):
                if first_stoptime_offset + dt <= to_date:
                    count += 1
            dt += a_day
        
        return count

class Arrangements(models.Model):
    desc = models.TextField()

class StopTime(models.Model):
    trip = models.ForeignKey('Trip')
    arrival_time = models.IntegerField(null=True)
    arrival_days = models.IntegerField(null=True)
    departure_time = models.IntegerField(null=True)
    departure_days = models.IntegerField(null=True)
    stop = models.ForeignKey('Stop')
    sequence = models.IntegerField()
    headsign = models.TextField()
    pickup_type = models.ForeignKey('Arrangements', related_name="pickup", null=True)
    drop_off_type = models.ForeignKey('Arrangements', related_name="dropoff", null=True)
    shape_dist_travelled = models.FloatField(null=True)

class Service(models.Model):
    dataset = models.ForeignKey('Dataset')
    text_id = models.TextField(max_length=20, db_index=True)

    class Meta:
        unique_together = (("dataset", "text_id"))

    def active(self, dt):
        active = self.calendar.active(dt)
        for caldate in self.calendardate_set.all():
            exception_active, _active = caldate.active(dt)
            if exception_active:
                active = _active
        return active

class Calendar(models.Model):
    service = models.OneToOneField('Service')
    monday = models.BooleanField()
    tuesday = models.BooleanField()
    wednesday = models.BooleanField()
    thursday = models.BooleanField()
    friday = models.BooleanField()
    saturday = models.BooleanField()
    sunday = models.BooleanField()
    start_date = models.DateField()
    end_date = models.DateField()
    weekday_index = [ 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday' ]

    def active(self, dt):
        return getattr(self, self.weekday_index[dt.weekday()]) and \
            (dt >= self.start_date) and \
            (dt <= self.end_date)
    
class CalendarDate(models.Model):
    service = models.ForeignKey('Service')
    date = models.DateField()
    add_or_remove_service = models.BooleanField()

    def active(self, dt):
        return dt == self.date, self.add_or_remove_service

class PaymentMethod(models.Model):
    desc = models.TextField()

class TransferPermission(models.Model):
    desc = models.TextField()
    limited = models.BooleanField()
    ntransfers = models.IntegerField()

class FareRule(models.Model):
    agency = models.ForeignKey('Agency')
    text_id = models.CharField(max_length=20, db_index=True)
    price = models.FloatField()
    currency_type = models.CharField(max_length=3)
    payment_method = models.ForeignKey('PaymentMethod')
    transfer_permission = models.ForeignKey('TransferPermission')
    transfer_duration = models.IntegerField()

class Zone(models.Model):
    text_id = models.TextField(max_length=20, db_index=True)

class RouteFareRules(models.Model):
    route = models.ForeignKey('Route')
    rule = models.ForeignKey('FareRule')

class OriginFareRules(models.Model):
    zone = models.ForeignKey('Zone')
    rule = models.ForeignKey('FareRule')

class DestinationFareRules(models.Model):
    zone = models.ForeignKey('Zone')
    rule = models.ForeignKey('FareRule')

class ContainsFareRules(models.Model):
    zone = models.ForeignKey('Zone')
    rule = models.ForeignKey('FareRule')

class Shape(models.Model):
    dataset = models.ForeignKey('Dataset')
    text_id = models.CharField(max_length=20, db_index=True)
    line_string = models.LineStringField()

    def frequency(self, from_date, to_date):
        f = 0
        for trip in self.trip_set.all():
            service = trip.service
            dt = from_date
            while dt <= to_date:
                if service.active(dt):
                    f += 1
                dt += datetime.timedelta(days=1)
        return f

class Segment(models.Model):
    dataset = models.ForeignKey('Dataset')
    line_string = models.LineStringField()
    min_lat = models.FloatField()
    max_lat = models.FloatField()
    min_lon = models.FloatField()
    max_lon = models.FloatField()

    def __eq__(self, other):
        return other.dataset == self.dataset and \
            other.line_string == self.line_string

class ShapeSegments(models.Model):
    shape = models.ForeignKey('Shape')
    segment = models.ForeignKey('Segment')
    reverse = models.BooleanField()
    position = models.IntegerField()

class Frequency(models.Model):
    trip = models.ForeignKey('Trip')
    start_time = models.IntegerField(null=True)
    start_time_days = models.IntegerField(null=True)
    end_time = models.IntegerField(null=True)
    end_time_days = models.IntegerField(null=True)
    headway_secs = models.IntegerField()

class TransferType(models.Model):
    desc = models.TextField()

class Transfers(models.Model):
    from_stop = models.ForeignKey('Stop', related_name='transfer_from_stop')
    to_stop = models.ForeignKey('Stop', related_name='transfer_to_stop')
    transfer_type = models.ForeignKey('TransferType')
    min_transfer_time = models.IntegerField(null=True)
